package com.gitee.wsl.transform.encode.base

import com.gitee.wsl.ext.string.toByteArray
import com.gitee.wsl.io.buildPacket
import com.gitee.wsl.io.readAvailable
import com.gitee.wsl.transform.encode.ByteArrayEncode
import com.gitee.wsl.transform.encode.ByteArrayEncodeDecode
import com.gitee.wsl.transform.encode.DecodeToByteArray
import com.gitee.wsl.transform.encode.DecodeToString
import com.gitee.wsl.transform.encode.StringEncode
import com.gitee.wsl.transform.encode.StringToStringEncodeDecode
import kotlinx.io.Source
import kotlinx.io.readByteArray
import kotlin.experimental.and
import kotlin.io.encoding.ExperimentalEncodingApi


const val BASE64_BLOCK_SIZE = 6
const val BASE64_PADDING_SIZE = 4

internal object Base64: StringToStringEncodeDecode {
    override fun encode(src: String): String = src.encodeBase64ToString()

    override fun decode(src: String): String = src.decodeBase64ToString()

}


val StringEncode.Companion.base64:StringEncode<String> get() = Base64

val DecodeToString.Companion.base64:DecodeToString<String> get() = Base64

fun String.encodeBase64ToString() = toByteArray().encodeBase64()

fun String.decodeBase64ToString() = decodeBase64().decodeToString()

//fun String.encodeBase64ToString(
//    dict: String = BASE64_DICT,
//    needPadding: Boolean = true
//) = toByteArray().encodeBase64(dict, needPadding)
//
//fun String.decodeBase64ToString(dict: String = BASE64_DICT) = decodeBase64(dict).decodeToString()


val DecodeToByteArray.Companion.base64: DecodeToByteArray<ByteArray>
    get() = DecodeToByteArray{src: ByteArray ->  src.decodeBase64()}

//fun ByteArray.decodeBase64(dict: String = BASE64_DICT) = decodeToString().decodeBase64(dict)

fun ByteArray.decodeBase64() = decodeToString().decodeBase64()

internal object ByteArrayBase64: ByteArrayEncodeDecode<String> {
    override fun encode(src: ByteArray): String = src.encodeBase64()

    override fun decode(src: String): ByteArray = src.decodeBase64()

}

val ByteArrayEncode.Companion.base64:ByteArrayEncode<String>
    get() = ByteArrayBase64

val DecodeToByteArray.Companion.base64FromString: DecodeToByteArray<String>
    get() = ByteArrayBase64

@OptIn(ExperimentalEncodingApi::class)
fun ByteArray.encodeBase64() = kotlin.io.encoding.Base64.encode(this)

@OptIn(ExperimentalEncodingApi::class)
fun String.decodeBase64() = kotlin.io.encoding.Base64.decode(this)

//
//fun ByteArray.encodeBase64(dict: String = BASE64_DICT, needPadding: Boolean = true) =
//    toBinaryString()
//        .chunked(BASE64_BLOCK_SIZE)
//        .joinToString("") {
//            dict.ifEmpty { BASE64_DICT }[it.padding("0", BASE64_BLOCK_SIZE).toInt(2)].toString()
//        }
//        .run {
//            // lcm (6, 8) /6 = 4
//            if (needPadding) {
//                padding("=".takeUnless { dict.contains(it) } ?: "＝", BASE64_PADDING_SIZE)
//            } else {
//                this
//            }
//        }
//
//fun String.decodeBase64(dict: String = BASE64_DICT) =
//    stripAllSpace() // remove all space  RFC 2045定义，每行为76个字符，行末加入\r\n
//        .asIterable()
//        .filter { (!dict.contains("=") && it != '=') || dict.contains(it) }
//        .joinToString("") {
//            dict
//                .ifEmpty { BASE64_DICT }
//                .indexOf(it)
//                .toString(2)
//                .padding("0", BASE64_BLOCK_SIZE, false)
//        }
//        .chunked(BYTE_BITS)
//        .filter { it.length == BYTE_BITS }
//        .map { (it.toInt(2) and BYTE_MASK).toByte() }
//        .toByteArray()
//

object Base64Url:StringToStringEncodeDecode{
    override fun encode(src: String): String = src.encodeBase64UrlToString()

    override fun decode(src: String): String = src.decodeBase64UrlToString()

}

val StringEncode.Companion.base64Url:StringEncode<String> get() = Base64Url

val DecodeToString.Companion.base64Url:DecodeToString<String> get() = Base64Url


fun String.encodeBase64UrlToString() = toByteArray().encodeBase64Url()

fun String.decodeBase64UrlToString() //= String(base64UrlDecode(dict))
        = decodeBase64Url().decodeToString()

/** 标准的Base64并不适合直接放在URL里传输，因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式， */
//fun String.encodeBase64UrlToString(dict: String = BASE64_URL_DICT) = toByteArray().encodeBase64Url(dict)
//
//fun String.decodeBase64UrlToString(dict: String = BASE64_URL_DICT) //= String(base64UrlDecode(dict))
//        = decodeBase64Url(dict).decodeToString()

object ByteArrayBase64Url: ByteArrayEncodeDecode<String> {
    override fun encode(src: ByteArray): String = src.encodeBase64Url()
    override fun decode(src: String): ByteArray = src.decodeBase64Url()
}

val ByteArrayEncode.Companion.base64Url:ByteArrayEncode<String>
    get() = ByteArrayBase64Url

val DecodeToByteArray.Companion.base64Url:DecodeToByteArray<String>
    get() = ByteArrayBase64Url

@OptIn(ExperimentalEncodingApi::class)
fun ByteArray.encodeBase64Url() = kotlin.io.encoding.Base64.UrlSafe.encode(this)

@OptIn(ExperimentalEncodingApi::class)
fun String.decodeBase64Url() = kotlin.io.encoding.Base64.UrlSafe.decode(this)

//fun String.decodeBase64Url(dict: String = BASE64_URL_DICT) =
//    decodeBase64(dict.ifEmpty { BASE64_URL_DICT })
//
//fun ByteArray.encodeBase64Url(dict: String = BASE64_URL_DICT) =
//    encodeBase64(dict.ifEmpty { BASE64_URL_DICT }, false)

internal fun String.padding(char: String, block: Int, isAfter: Boolean = true) =
    chunked(block).joinToString("") {
        it.takeIf { it.length == block }
            ?: if (isAfter) {
                it + char.repeat(block - it.length)
            } else {
                char.repeat(block - it.length) + it
            }
    }

@OptIn(ExperimentalEncodingApi::class)
fun ByteArray.encodeBase64Mine() = kotlin.io.encoding.Base64.Mime.encode(this)

@OptIn(ExperimentalEncodingApi::class)
fun String.decodeBase64Mime() = kotlin.io.encoding.Base64.Mime.decode(this)


/**
 * Encode [ByteReadPacket] in base64 format
 *
 * [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.util.encodeBase64)
 */
fun Source.encodeBase64(): String = readByteArray().encodeBase64()


/**
 * Decode [ByteReadPacket] from base64 format
 *
 * [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.util.decodeBase64Bytes)
 */
fun Source.decodeBase64Bytes() = buildPacket {
    val data = ByteArray(4)

    while (!exhausted()) {
        val read = readAvailable(data)

        val chunk = data.foldIndexed(0) { index, result, current ->
            result or (current.fromBase64().toInt() shl ((3 - index) * 6))
        }

        for (index in data.size - 2 downTo (data.size - read)) {
            val origin = (chunk shr (8 * index)) and 0xff
            writeByte(origin.toByte())
        }
    }
}

private const val BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
private const val BASE64_MASK: Byte = 0x3f

private val BASE64_INVERSE_ALPHABET = IntArray(256) {
    BASE64_ALPHABET.indexOf(it.toChar())
}.also {
    // correctly decode URL-safe Base64
    it['-'.code] = it['+'.code]
    it['_'.code] = it['/'.code]
}


@Suppress("NOTHING_TO_INLINE")
internal inline fun Int.toBase64(): Char = BASE64_ALPHABET[this]

@Suppress("NOTHING_TO_INLINE")
internal inline fun Byte.fromBase64(): Byte = BASE64_INVERSE_ALPHABET[toInt() and 0xff].toByte() and BASE64_MASK


